import stroke from './stroke';
import { DotType } from './pen';

/**
 * @name TQL笔迹绘制 
 */
export default class TqlCanvas {
    /** canvas 缩放比例 */
    static canvasScaling = 2
    /** b5纸 宽度 */
    static b5_width = 182
    /** b5纸 高度 */
    static b5_height = 256
    /** 码点宽度 */
    static x_point_size = 1.524
    /** 码点高度 */
    static y_point_size = 1.524
    /**
     * @name 创建离屏canvas
     * @param {number} width canvas的宽度
     * @param {number} height canvas的高度
     * @returns {HTMLCanvasElement}
     */
    static offScreenCanvas(width, height) {
        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        return canvas;
    }
    /**
     * @name dotType转number
     * @param {'PEN_DOWN' | 'PEN_MOVE' | 'PEN_UP'} dotType 点类型
     * @returns {number}
     */
    static dotType2Number(dotType) {
        return ['PEN_DOWN', 'PEN_MOVE', 'PEN_UP'].indexOf(dotType)
    }
    /**
     * @name 四舍五入
     * @param {number} number 需四舍五入的数字
     * @returns {number}
     */
    static roundNumber(number) {
        return Math.round(number * Math.pow(10, 15)) / Math.pow(10, 15)
    }
    /**
     * @param {HTMLCanvasElement} canvas 传入的显屏canvas
     */
    constructor(canvas) {
        //constant @------------------------------------------------------------------------------
        //显屏canvas 
        this.canvas = canvas;
        //显屏canvas的context
        this.context = canvas.getContext('2d')
        //离屏canvas 宽
        this.width = canvas.width * TqlCanvas.canvasScaling
        //离屏canvas 高
        this.height = canvas.height * TqlCanvas.canvasScaling
        //离屏canvas
        this.osCanvas = TqlCanvas.offScreenCanvas(this.width, this.height)
        //离屏canvas的context
        this.osContext = this.osCanvas.getContext('2d')
        //variable @--------------------------------------------------------------------------------
        //线宽
        this.lineWidth = 2
        //线色
        this.lineColor = '#000000'
        //当前页码Id
        this.pageId = 0;
        //页码列表
        this.pageList = []
        //当前书本Id
        this.bookId = 0
        //笔迹数据列表
        this.penDataMap = new Map()
        //笔迹图片列表
        this.penImageMap = new Map()
        //上一个点数据
        this.lastPoint = this.point(0, 0, 0, 0, 1, 0, 0, 0)
        //背景图数据
        this.backroundMap = new Map([
            [100, document.getElementById('B100Right')]
        ])
        //symbol @--------------------------------------------------------------------------------------
        //是否回放
        this.isReplay = false
        //是否暂停
        this.isPauseReplay = false
        //回放定时器
        this.replayTimer = null
        //回放下标
        this.replayIndex = 0
        //回放百分比
        this.replayPercentage = 0
        //回放的当前页的图片
        this.replayBeforeImage = null
        //是否开启过滤算法
        this.isFilter = false
        //是否开启笔锋算法 
        this.isStroke = false
        //绘制背景图
        this.drawImage(this.backroundMap.get(100))
    }

    /**
     * @name 解析数据
     */
    parseData(dot) {
        let x = TqlCanvas.roundNumber(this.width * dot.x / (TqlCanvas.b5_width / dot.distance[0]))
        let y = TqlCanvas.roundNumber(this.height * dot.y / (TqlCanvas.b5_height / dot.distance[1]))
        return {
            type: dot.type,
            x,
            y,
            force: dot.force,
            pageId: dot.page + 1,
            bookId: dot.book,
            ownerId: dot.owner,
            angle: dot.angle
        }
    }

    /**
     * @name 点数据
     */
    point(data) {
        return { ...data, lineWidth: this.lineWidth, lineColor: this.lineColor }
    }

    /**
     * @name 处理点 
     * @param {object} data 数据
     */
    handlePoint(data) {
        //回放 禁止操作
        if (this.isReplay) {
            console.log('回放中');
            return
        }
        //当前点数据
        const currentPoint = this.point(this.parseData(data))
        //pageId bookId 与当前点不同时
        // if (currentPoint.pageId !== this.pageId || currentPoint.bookId !== this.bookId) {
        //     //当前页有数据就 先绘制出来
        //     if (this.penImageMap.has(currentPoint.pageId)) { //有就先绘制
        //         //切到当前页
        //         this.cutPage(currentPoint.pageId)
        //     } else { //没有就存
        //         this.penImageMap.set(this.pageId, this.saveImageData())
        //         this.drawImage(this.backroundMap.get(100))
        //     }
        // }
        //存储笔迹数据 和 页码数据
        this.savePageData(currentPoint)
        //更新 pageId 和 bookId
        this.pageId = currentPoint.pageId
        this.bookId = currentPoint.bookId
        //绘制
        this.draw(currentPoint)
    }

    /**
     * @name 绘制
     * @param {object} point 点数据
     */
    draw(point) {
        if (point.type !== 0 && this.lastPoint.x === 0 && this.lastPoint.y === 0) this.lastPoint = point
        //笔锋绘制
        if (this.isStroke) {
            stroke(point.type, point.x, point.y, point.force, this.osContext, point.lineWidth, point.lineColor)
            this.synchronousDraw()
        } else { //正常绘制
            switch (point.type) {
                case DotType.Down: //down点
                    this.lastPoint = point
                    break;
                case DotType.Move: //move点
                case DotType.Up: //up点
                    this.drawLine(this.lastPoint, point)
                    this.lastPoint = point
                    break;
            }
        }
    }

    /**
     * @name 绘制线
     * @param {object} lastPoint 上一个点数据
     * @param {object} currentPoint 当前点数据
     */
    drawLine(lastPoint, currentPoint) {
        this.osContext.beginPath(); //起始一条路径，或重置当前路径
        this.osContext.lineWidth = currentPoint.lineWidth;
        this.osContext.strokeStyle = currentPoint.lineColor;
        this.osContext.moveTo(lastPoint.x, lastPoint.y); // 起点 
        this.osContext.lineTo(currentPoint.x, currentPoint.y); // 终点
        // this.osContext.closePath(); //创建从当前点回到起始点的路径
        this.osContext.stroke(); //绘制已定义的路径
        this.synchronousDraw()
    }

    /**
     * @name 离屏canvas和显屏canvas同步绘制
     */
    synchronousDraw() {
        //显屏canvas清空
        this.context.clearRect(0, 0, this.width, this.height)
        //离屏canvas画入显屏canvas
        this.context.drawImage(this.osCanvas, 0, 0, this.width / 2, this.height / 2)
    }

    /**
     * @name 存储页码数据
     * @param {object} point 点数据
     */
    savePageData(point) {
        let pageId = point.pageId
        if (this.penDataMap.has(pageId)) {
            //存在 将点添加即可
            this.penDataMap.get(pageId).push(point)
        } else {
            //不存在 添加新的pageId数据 
            this.penDataMap.set(pageId, [point])
            //更新pagelist
            this.pageList = [...this.penDataMap.keys()]
        }
    }

    /**
     * @name 生成图片
     */
    saveImageData() {
        let src = this.osCanvas.toDataURL('image/png')
        let image = new Image()
        image.src = src
        return image
    }

    /**
     * @name 切页
     */
    cutPage(pageId) {
        //回放 禁止操作
        if (this.isReplay) return
        //切页之前存一遍
        this.penImageMap.set(this.pageId, this.saveImageData())
        //清除当前屏幕
        this.osContext.clearRect(0, 0, this.width, this.height)
        //绘制 已有的 数据
        this.osContext.drawImage(this.penImageMap.get(pageId), 0, 0, this.width, this.height)
        //显屏同步
        this.synchronousDraw()
        //更pageId
        this.pageId = pageId
    }


    /**
     * @name 绘制背景图
     */
    drawImage(imageElement) {
        this.osContext.clearRect(0, 0, this.width, this.height)
        this.osContext.drawImage(imageElement, 0, 0, this.width, this.height)
        this.synchronousDraw()
    }

    /**
     * @name 清屏
     * @returns status: 0-失败 / 1-成功 ,  msg: 提示信息
     */
    clearScreen() {
        return new Promise((resolve) => {
            //回放 禁止操作
            if (this.isReplay) {
                resolve({ status: 0, msg: '正在回放，请勿清屏' })
            } else {
                this.drawImage(this.backroundMap.get(100))
                this.penDataMap.clear()
                this.penImageMap.clear()
                this.pageList = []
                resolve({ status: 1, msg: '清屏成功' })
            }
        })
    }

    /**
     * @name 回放
     * @returns operate: 0-回放 / 1-暂停 / 2-继续, status: 0-失败/1-成功 ,msg: 提示信息, error: 错误信息
     */
    replay() {
        if (this.isReplay) { //回放
            if (this.isPauseReplay) { //暂停
                return new Promise((resolve) => {
                    this.continueReplay().then(() => {
                        resolve({ operate: 2, status: 1, msg: '继续回放成功' })
                    }).catch((error) => {
                        resolve({ operate: 2, status: 0, msg: '继续回放失败，程序出现异常。', error })
                    })
                })
            } else { //非暂停
                return new Promise((resolve) => {
                    this.pauseReplay().then(() => {
                        resolve({ operate: 1, status: 1, msg: '暂停回放成功' })
                    }).catch((error) => {
                        resolve({ operate: 1, status: 0, msg: '暂停回放失败，程序出现异常。', error })
                    })
                })
            }
        } else { //回放
            return new Promise((resolve) => {
                this.startReplay().then((data) => {
                    resolve(data)
                }).catch((error) => {
                    resolve({ operate: 0, status: 0, msg: '回放失败，程序出现异常。', error })
                })
            })
        }
    }

    /** 
     * @name 计算回放百分比
     */
    calcReplayPercentage(currentIndex, total) {
        return Math.ceil(currentIndex / total * 100)
    }

    /**
     * @name 开始回放
     */
    startReplay() {
        return new Promise((resolve) => {
            const currentPageData = this.penDataMap.get(this.pageId)
            if (!currentPageData || currentPageData.length === 0) {
                resolve({ operate: 0, status: 0, msg: '回放失败，当前页无数据！' })
            } else {
                //打开回放开关
                this.isReplay = true
                this.replayBeforeImage = this.saveImageData()
                this.drawImage(this.backroundMap.get(100))
                this.replayTimer = setInterval(() => {
                    this.draw(currentPageData[this.replayIndex])
                    this.replayPercentage = this.calcReplayPercentage(this.replayIndex, currentPageData.length)
                    this.replayIndex++
                    if (this.replayIndex === currentPageData.length - 1) {
                        this.cancelReplay()
                    }
                }, 10);
                resolve({ operate: 0, status: 1, msg: '正在回放中' })
            }
        })
    }

    /**
     * @name 暂停回放
     */
    pauseReplay() {
        return new Promise((resolve) => {
            //打开暂停开关
            this.isPauseReplay = true
            //清除定时器
            clearInterval(this.replayTimer)
            resolve()
        })
    }

    /**
     * @name 继续回放
     */
    continueReplay() {
        return new Promise((resolve) => {
            resolve()
            //关闭暂停开关
            this.isPauseReplay = false
            const currentPageData = this.penDataMap.get(this.pageId)
            this.replayTimer = setInterval(() => {
                this.draw(currentPageData[this.replayIndex])
                this.replayPercentage = this.calcReplayPercentage(this.replayIndex, currentPageData.length)
                this.replayIndex++
                if (this.replayIndex === currentPageData.length - 1) {
                    this.cancelReplay()
                }
            }, 10);
        })
    }

    /**
     * @name 取消回放
     */
    cancelReplay() {
        return new Promise((resolve) => {
            new Promise((resolve) => {
                //关闭回放开关
                this.isReplay = false
                //关闭暂停开关
                this.isPauseReplay = false
                //清零回放下标
                this.replayIndex = 0
                //清除定时器
                clearInterval(this.replayTimer)
                //无论是正常结束回放 或 手动结束回放 都需将本页数据重新绘制一遍
                this.drawImage(this.replayBeforeImage)
                //更改promise状态
                resolve()
            }).then(() => {
                resolve({ status: 1, msg: '取消回放成功' })
            }).catch((error) => {
                resolve({ status: 0, msg: '取消回放失败', error })
            })
        })
    }
}